home *** CD-ROM | disk | FTP | other *** search
- Path: bloom-beacon.mit.edu!gatech!howland.reston.ans.net!pipex!uunet!library.erc.clarkson.edu!sun.soe.clarkson.edu!cline
- From: cline@sun.soe.clarkson.edu (Marshall Cline)
- Newsgroups: comp.lang.c++
- Subject: C++ FAQ: posting #3/4
- Followup-To: comp.lang.c++
- Date: 14 Nov 1994 00:26:37 GMT
- Organization: Paradigm Shift, Inc (training/consulting/OOD/OOP/C++)
- Lines: 639
- Sender: cline@sun.soe.clarkson.edu
- Distribution: world
- Expires: +1 month
- Message-ID: <3a6art$gd8@library.erc.clarkson.edu>
- Reply-To: cline@parashift.com (Marshall Cline)
- NNTP-Posting-Host: sun.soe.clarkson.edu
- Summary: Please read this before posting to comp.lang.c++
-
- comp.lang.c++ Frequently Asked Questions list (with answers, fortunately).
- Copyright (C) 1991-94 Marshall P. Cline, Ph.D.
- Posting 3 of 4.
- Posting #1 explains copying permissions, (no)warranty, table-of-contents, etc
-
- ==============================================================================
- SECTION 14: Style guidelines
- ==============================================================================
-
- Q74: What are some good C++ coding standards?
-
- Thank you for reading this answer rather than just trying to set your own
- coding standards.
-
- But please don't ask this question on comp.lang.c++. Nearly every software
- engineer has, at some point, felt that coding standards are or can be used as a
- "power play." Furthermore some attempts to set C++ coding standards have been
- made by those unfamiliar with the language and/or paradigm, so the standards
- end up being based on what WAS the state-of-the-art when the standards setters
- where writing code. Such impositions generate an attitude of mistrust for
- coding standards.
-
- Obviously anyone who asks this question on comp.lang.c++ wants to be trained so
- they DON'T run off on their own ignorance, but nonetheless the answers tend to
- generate more heat than light.
-
- ==============================================================================
-
- Q75: Are coding standards necessary? Are they sufficient?
-
- Coding standards do not make non OO programmers into OO programmers; only
- training and experience do that. If coding standards have merit, it is that
- they discourage the petty fragmentation that occurs when large organizations
- coordinate the activities of diverse groups of programmers.
-
- But you really want more than a coding standard. The structure provided by
- coding standards gives neophytes one less degree of freedom to worry about,
- however pragmatics go well beyond pretty-printing standards. Organizations
- need a consistent PHILOSOPHY of design and implementation. E.g., strong or
- weak typing? references or ptrs in interfaces? stream I/O or stdio? should
- C++ code call our C? vise versa? how should ABCs be used? should inheritance
- be used as an implementation technique or as a specification technique? what
- testing strategy should be employed? inspection strategy? should interfaces
- uniformly have a "get" and/or "set" method for each data member? should
- interfaces be designed from the outside-in or the inside-out? should errors be
- handled by try/catch/throw or return codes? etc.
-
- What is needed is a "pseudo standard" for detailed DESIGN. I recommend a
- three-pronged approach to achieving this standardization: training, mentoring,
- and libraries. Training provides "intense instruction," mentoring allows OO to
- be caught rather than just taught, and a high quality C++ class library
- provides "long term instruction." There is a thriving commercial market for
- all three kinds of "training." Advice by organizations who have been through
- the mill is consistent: Buy, Don't Build. Buy libraries, buy training, buy
- tools, buy consulting. Companies who have attempted to become a self-taught
- tool-shop as well as an application/system shop have found success difficult.
-
- Few argue that coding standards are "ideal," or even "good," however they are
- necessary in the kind of organizations/situations described above.
-
- The following FAQs provide some basic guidance in conventions and styles.
-
- ==============================================================================
-
- Q76: Should our organization determine coding standards from our C
- experience?
-
- No!
-
- No matter how vast your C experience, no matter how advanced your C expertise,
- being a good C programmer does not make you a good C++ programmer. Converting
- from C to C++ is more than just learning the syntax and semantics of the "++"
- part of C++. Organizations who want the promise of OOP, but who fail to put
- the "OO" into OOP, are fooling themselves; the balance sheet will show their
- folly.
-
- C++ coding standards should be tempered by C++ experts. Asking comp.lang.c++
- is a start (but don't use the term "coding standard" in the question; instead
- simply say, "what are the pros and cons of this technique?"). Seek out experts
- who can help guide you away from pitfalls. Get training. Buy libraries and
- see if "good" libraries pass your coding standards. Do NOT set standards by
- yourself unless you have considerable experience in C++. Having no standard is
- better than having a bad standard, since improper "official" positions "harden"
- bad brain traces. There is a thriving market for both C++ training and
- libraries from which to pool expertise.
-
- One more thing: whenever something is in demand, the potential for charlatans
- increases. Look before you leap. Also ask for student-reviews from past
- companies, since not even expertise makes someone a good communicator.
- Finally, select a practitioner who can teach, not a full time teacher who has a
- passing knowledge of the language/paradigm.
-
- ==============================================================================
-
- Q77: Should I declare locals in the middle of a fn or at the top?
-
- Declare near first use.
-
- An object is initialized (constructed) the moment it is declared. If you don't
- have enough information to initialize an object until half way down the fn, you
- can either initialize it to an "empty" value at the top then "assign" it later,
- or initialize it correctly half way down the fn. It's cheaper (in runtime
- performance) to get it right the first time than to build it once, tear it
- down, and build it again. Simple examples show a factor of 350% speed hit for
- simple classes like "String". Your mileage may vary; surely the overall system
- degradation will be less that 300+%, but there WILL be degradation.
- UNNECESSARY degradation.
-
- A common retort to the above is: "we"ll provide "set" methods for every datum
- in our objects, so the cost of construction will be spread out." This is worse
- than the performance overhead, since now you're introducing a maintenance
- nightmare. Providing "set" methods for every datum is tantamount to public
- data: you've exposed your implementation technique to the world. The only
- thing you've hidden is the physical NAMES of your member objects, but the fact
- that you're using a List and a String and a float (for example) is open for all
- to see. Maintenance generally consumes far more resources than run-time CPU.
-
- Locals should be declared near their first use. Sorry that this isn't familiar
- to C experts, but "new" doesn't necessarily mean "bad."
-
- ==============================================================================
-
- Q78: What source-file-name convention is best? "foo.C"? "foo.cc"? "foo.cpp"?
-
- If you already have a convention, use it. If not, consult your compiler to see
- what the compiler expects. Typical answers are: ".C", ".cc", ".cpp", or
- ".cxx" (naturally the ".C" extension assumes a case-sensitive file system to
- distinguish ".C" from ".c").
-
- At Paradigm Shift, Inc., we use ".C" in our Makefiles even on case-insensitive
- file systems (on case-insensitive file systems, we supply the compiler option
- that means "assume all .c files are C++ source files"; e.g., "-Tdp" for IBM
- CSet++, "-cpp" for Zortech C++, "-P" for Borland C++, etc).
-
- ==============================================================================
-
- Q79: What header-file-name convention is best? "foo.H"? "foo.hh"? "foo.hpp"?
-
- If you already have a convention, use it. If not, and if you don't need your
- editor to distinguish between C and C++ files, simply use ".h". Otherwise use
- whatever the editor wants, such as ".H", ".hh", or ".hpp".
-
- At Paradigm Shift, Inc., we use ".h" for both C and C++ source files (then
- again, we don't create many straight C header files).
-
- ==============================================================================
-
- Q80: Are there any lint-like guidelines for C++?
-
- Yes, there are some practices which are generally considered dangerous.
- However none of these are universally "bad," since situations arise when
- even the worst of these is needed:
- * a class "Fred"s assignment operator should return "*this" as an "Fred&"
- (allows chaining of assignments)
- * a class with any virtual fns ought to have a virtual destructor
- * a class with any of {destructor, assignment operator, copy constructor}
- generally needs all 3
- * a class "Fred"s copy constructor and assignment operator should have "const"
- in the parameter: respectively "Fred::Fred(const Fred&)" and
- "Fred& Fred::operator=(const Fred&)".
- * always use initialization lists for class sub-objects rather than assignment
- the performance difference for user-defined classes can be substantial (3x!)
- * many assignment operators should start by testing if "we" are "them"; e.g.,
- Fred& Fred::operator= (const Fred& fred)
- {
- if (this == &fred) return *this;
- //...normal assignment duties...
- return *this;
- }
- sometimes there is no need to check, but these situations generally
- correspond to when there's no need for an explicit user-specified assignment
- op (as opposed to a compiler-synthesized assignment-op).
- * in classes that define both "+=," "+" and "=," "a+=b" and "a=a+b" should
- generally do the same thing; ditto for the other identities of builtin types
- (e.g., a+=1 and ++a; p[i] and *(p+i); etc). This can be enforced by writing
- the binary ops using the "op=" forms; e.g.,
- Fred operator+ (const Fred& a, const Fred& b)
- {
- Fred ans = a;
- ans += b;
- return ans;
- }
- This way the "constructive" binary ops don't even need to be friends. But
- it is sometimes possible to more efficiently implement common ops (e.g., if
- class "Fred" is actually "String," and "+=" has to reallocate/copy string
- memory, it may be better to know the eventual length from the beginning).
-
- ==============================================================================
- SECTION 15: Keys for Smalltalk programmers to learn C++
- ==============================================================================
-
- Q81: Why does C++'s FAQ have a section on Smalltalk? Is this
- Smalltalk-bashing?
-
- The two "major" OOPLs in the world are C++ and Smalltalk. Due to its
- popularity as the OOPL with the second largest user pool, many new C++
- programmers come from a Smalltalk background. This section answers the
- questions:
- * what's different about the two languages
- * what must a Smalltalk-turned-C++ programmer know to master C++
-
- This section does *!*NOT*!* attempt to answer the questions:
- * which language is "better"?
- * why is Smalltalk "bad"?
- * why is C++ "bad"?
-
- Nor is it an open invitation for some Smalltalk terrorist to slash my tires
- while I sleep (on those rare occasions when I have time to rest these days :-).
-
- ==============================================================================
-
- Q82: What's the difference between C++ and Smalltalk?
-
- The most important differences are:
-
- * static typing vs dynamic typing?
- * must inheritance be used only for subtyping?
- * value vs reference semantics?
-
- The first two differences are illuminated in the remainder of this section; the
- third point is the subject of the section that follows.
-
- If you're a Smalltalk programmer who wants to learn C++, you'd be very wise to
- study the next three FAQs carefully.
-
- ==============================================================================
-
- Q83: What is "static typing", and how is it similar/dissimilar to Smalltalk?
-
- Static typing says the compiler checks the type-safety of every operation
- STATICALLY (at compile-time), rather than to generate code which will check
- things at run-time. For example, with static typing, the signature matching of
- fn arguments is checked, and an improper match is flagged as an error by the
- COMPILER, not at run-time.
-
- In OO code, the most common "typing mismatch" is invoking a member function
- against an object which isn't prepared to handle the operation. E.g., if class
- "Fred" has member fn "f()" but not "g()", and "fred" is an instance of class
- "Fred", then "fred.f()" is legal and "fred.g()" is illegal. C++ (statically
- typed) catches the error at compile time, and Smalltalk (dynamically typed)
- catches the error at run-time. (Technically speaking, C++ is like Pascal
- --PSEUDO statically typed-- since ptr casts and unions can be used to violate
- the typing system; which reminds me: only use ptr casts and unions as often as
- you use "goto"s).
-
- ==============================================================================
-
- Q84: Which is a better fit for C++: "static typing" or "dynamic typing"?
-
- If you want to use C++ most effectively, use it as a statically typed language.
-
- C++ is flexible enough that you can (via ptr casts, unions, and #defines) make
- it "look" like Smalltalk. But don't. Which reminds me: try to avoid #define.
-
- There are places where ptr casts and unions are necessary and even wholesome,
- but they should be used carefully and sparingly. A ptr cast tells the compiler
- to believe you. An incorrect ptr cast might corrupt your heap, scribble into
- memory owned by other objects, call nonexistent methods, and cause general
- failures. It's not a pretty sight. If you avoid these and related constructs,
- you can make your C++ code both safer and faster, since anything that can be
- checked at compile time is something that doesn't have to be done at run-time.
-
- Even if you're in love with dynamic typing, please avoid it in C++, or else
- please consider using another language that better supports your desire to
- defer typing decisions to run-time. C++ performs 100% of its type checking at
- compile time; it has NO built-in mechanism to do ANY type checking at run-time.
- If you use C++ as a dynamically typed OOPL, your life is in your own hands.
-
- ==============================================================================
-
- Q85: How can you tell if you have a dynamically typed C++ class library?
-
- Hint #1: when everything is derived from a single root class, usually "Object."
- Hint #2: when the container classes (List, Stack, Set, etc) are non-templates.
- Hint #3: when the container classes (List, Stack, Set, etc) insert/extract
- elements as pointers to "Object" (you can put an Apple into such a container,
- but when you get it out, the compiler only knows that it is derived from
- Object, so you have to use a pointer cast to convert it back to an Apple*; and
- you better pray a lot that it really IS an Apple, cause your blood is on your
- own head).
-
- You can make the pointer cast "safe" by using "dynamic_cast" (see earlier), but
- this dynamic testing is just that: dynamic. This coding style is the essence
- of dynamic typing in C++. You call a function that says "convert this Object
- into an Apple or give me NULL if its not an Apple," and you've got dynamic
- typing: you don't know what will happen until run-time.
-
- When you use with templates to implement your containers, the C++ compiler can
- statically validate 99% of an application's typing information (the figure
- "99%" is apocryphal; some claim they always get 100%, those who need
- persistence get something less than 100% static type checking). The point is:
- C++ gets genericity from templates, not from inheritance.
-
- ==============================================================================
-
- Q86: How do you use inheritance in C++, and is that different from Smalltalk?
-
- Some people believe that the purpose of inheritance is code reuse. In C++,
- this is wrong. Stated plainly, "inheritance is not 'for' code reuse."
-
- The purpose of inheritance in C++ is to express interface compliance
- (subtyping), not to get code reuse. In C++, code reuse usually comes via
- composition rather than via inheritance. In other words, inheritance is mainly
- a specification technique rather than an implementation technique.
-
- This is a major difference with Smalltalk, where there is only one form of
- inheritance (C++ provides "private" inheritance to mean "share the code but
- don't conform to the interface", and "public" inheritance to mean "kind-of").
- The Smalltalk language proper (as opposed to coding practice) allows you to
- have the EFFECT of "hiding" an inherited method by providing an override that
- calls the "does not understand" method. Furthermore Smalltalk allows a
- conceptual "is-a" relationship to exist APART from the subclassing hierarchy
- (subtypes don't have to be subclasses; e.g., you can make something that is-a
- Stack yet doesn't inherit from class Stack).
-
- In contrast, C++ is more restrictive about inheritance: there's no way to make
- a "conceptual is-a" relationship without using inheritance (the C++ work-around
- is to separate interface from implementation via ABCs). The C++ compiler
- exploits the added semantic information associated with public inheritance to
- provide static typing.
-
- ==============================================================================
-
- Q87: What are the practical consequences of diffs in Smalltalk/C++
- inheritance?
-
- Smalltalk lets you make a subtype that isn't a subclass, and allows you to make
- a subclass that isn't a subtype. This allows Smalltalk programmers to be very
- carefree in putting data (bits, representation, data structure) into a class
- (e.g., you might put a linked list into a Stack class). After all, if someone
- wants an array-based-Stack, they don't have to inherit from Stack; they could
- inherit such a class from Array if desired, even though an ArrayBasedStack is
- NOT a kind-of Array!
-
- In C++, you can't be nearly as carefree. Only mechanism (method code), but not
- representation (data bits) can be overridden in subclasses. Therefore you're
- usually better off NOT putting the data structure in a class. This leads to a
- stronger reliance on Abstract Base Classes (ABCs).
-
- I like to think of the difference between an ATV and a Maseratti. An ATV (all
- terrain vehicle) is more fun, since you can "play around" by driving through
- fields, streams, sidewalks, and the like. A Maseratti, on the other hand, gets
- you there faster, but it forces you to stay on the road. My advice to C++
- programmers is simple: stay on the road. Even if you're one of those people
- who like the "expressive freedom" to drive through the bushes, don't do it in
- C++; it's not a good fit.
-
- ==============================================================================
-
- Q88: Do you need to learn a "pure" OOPL before you learn C++?
-
- No (in fact, doing so might actually hurt you).
-
- (Note that Smalltalk is a "pure" OOPL, and C++ is a "hybrid" OOPL). Before
- reading this, please read the previous FAQs on the difference between C++ and
- Smalltalk.
-
- The "purity" of the OOPL doesn't make the transition to C++ any easier. In
- fact, the typical use of dynamic typing and non-subtyping inheritance can make
- it even harder for Smalltalk programmers to learn C++. Paradigm Shift, Inc.,
- has taught OO technology to literally thousands of people, and we have noticed
- that people who want to learn C++ from a Smalltalk background usually have just
- as hard a time as those who've never seen inheritance before. In fact, those
- with extensive experience with a dynamically typed OOPL (usually but not always
- Smalltalk) might even have a HARDER time, since it's harder to UNLEARN habits
- than it is to learn the statically typed way from the beginning.
-
- ==============================================================================
-
- Q89: What is the NIHCL? Where can I get it?
-
- NIHCL stands for "national-institute-of-health's-class-library."
- it can be acquired via anonymous ftp from [128.231.128.7]
- in the file pub/nihcl-3.0.tar.Z
-
- NIHCL (some people pronounce it "N-I-H-C-L," others pronounce it like "nickel")
- is a C++ translation of the Smalltalk class library. There are some ways where
- NIHCL's use of dynamic typing helps (e.g., persistent objects). There are also
- places where its use of dynamic typing creates tension with the static typing
- of the C++ language.
-
- See previous FAQs on Smalltalk for more.
-
- ==============================================================================
- SECTION 16: Reference and value semantics
- ==============================================================================
-
- Q90: What is value and/or reference semantics, and which is best in C++?
-
- With reference semantics, assignment is a pointer-copy (i.e., a REFERENCE).
- Value (or "copy") semantics mean assignment copies the value, not just the
- pointer. C++ gives you the choice: use the assignment operator to copy the
- value (copy/value semantics), or use a ptr-copy to copy a pointer (reference
- semantics). C++ allows you to override the assignment operator to do anything
- your heart desires, however the default (and most common) choice is to copy the
- VALUE.
-
- Pros of reference semantics: flexibility and dynamic binding (you get dynamic
- binding in C++ only when you pass by ptr or pass by ref, not when you pass by
- value).
-
- Pros of value semantics: speed. "Speed" seems like an odd benefit to for a
- feature that requires an object (vs a ptr) to be copied, but the fact of the
- matter is that one usually accesses an object more than one copies the object,
- so the cost of the occasional copies is (usually) more than offset by the
- benefit of having an actual object rather than a ptr to an object.
-
- There are three cases when you have an actual object as opposed to a pointer to
- an object: local vars, global/static vars, and fully contained member objects
- in a class. The most important of these is the last ("composition").
-
- More info about copy-vs-reference semantics is given in the next FAQs. Please
- read them all to get a balanced perspective. The first few have intentionally
- been slanted toward value semantics, so if you only read the first few of the
- following FAQs, you'll get a warped perspective.
-
- Assignment has other issues (e.g., shallow vs deep copy) which are not covered
- here.
-
- ==============================================================================
-
- Q91: What is "virtual data," and how-can / why-would I use it in C++?
-
- Virtual data allows a derived class to change the exact class of a base class's
- member object. Virtual data isn't strictly "supported" by C++, however it
- can be simulated in C++. It ain't pretty, but it works.
-
- To simulate virtual data in C++, the base class must have a pointer to the
- member object, and the derived class must provide a "new" object to be pointed
- to by the base class's pointer. The base class would also have one or more
- normal constructors that provide their own referrent (again via "new"), and the
- base class's destructor would "delete" the referent.
-
- For example, class "Stack" might have an Array member object (using a pointer),
- and derived class "StretchableStack" might override the base class member data
- from "Array" to "StretchableArray". For this to work, StretchableArray would
- have to inherit from Array, so Stack would have an "Array*". Stack's normal
- constructors would initialize this "Array*" with a "new Array", but Stack would
- also have a (possibly "protected:") constructor that would accept an "Array*"
- from a derived class. StretchableArray's constructor would provide a "new
- StretchableArray" to this special constructor.
-
- Pros:
- * Easier implementation of StretchableStack (most of the code is inherited).
- * Users can pass a StretchableStack as a kind-of Stack.
-
- Cons:
- * Adds an extra layer of indirection to access the Array.
- * Adds some extra freestore allocation overhead (both new and delete).
- * Adds some extra dynamic binding overhead (reason given in next FAQ).
-
- In other words, we succeeded at making OUR job easier as the implementor of
- StretchableStack, but all our users pay for it. Unfortunately the extra
- overhead was imposed on both users of StretchableStack AND on users of Stack.
-
- See the FAQ after the next to find out how much the users "pay." Also: PLEASE
- read the few FAQs that follow the next one too (YOU WILL NOT GET A BALANCED
- PERSPECTIVE WITHOUT THE OTHERS).
-
- ==============================================================================
-
- Q92: What's the difference between virtual data and dynamic data?
-
- The easiest way to see the distinction is by an analogy with "virtual fns":
- A virtual member fn means the declaration (signature) must stay the same in
- subclasses, but the defn (body) can be overridden. The overriddenness of an
- inherited member fn is a static property of the subclass; it doesn't change
- dynamically throughout the life of any particular object, nor is it possible
- for distinct objects of the subclass to have distinct defns of the member fn.
-
- Now go back and re-read the previous paragraph, but make these substitutions:
- * "member fn" --> "member object"
- * "signature" --> "type"
- * "body" --> "exact class'
- After this, you'll have a working definition of virtual data.
-
- Another way to look at this is to distinguish "per-object" member functions
- from "dynamic" member functions. A "per-object" member fn is a member fn that
- is potentially different in any given instance of an object, and could be
- implemented by burying a function ptr in the object; this pointer could be
- "const", since the pointer will never be changed throughout the object's life.
- A "dynamic" member fn is a member fn that will change dynamically over time;
- this could also be implemented by a function ptr, but the fn ptr would not be
- const.
-
- Extending the analogy, this gives us three distinct concepts for data members:
- * virtual data: the defn ("class") of the member object is overridable in
- subclasses provided its declaration ("type") remains the same, and this
- overriddenness is a static property of the subclass.
- * per-object-data: any given object of a class can instantiate a different
- conformal (same type) member object upon initialization (usually a "wrapper"
- object), and the exact class of the member object is a static property of
- the object that wraps it.
- * dynamic-data: the member object's exact class can change dynamically over
- time.
-
- The reason they all look so much the same is that none of this is "supported"
- in C++. It's all merely "allowed," and in this case, the mechanism for faking
- each of these is the same: a ptr to a (probably abstract) base class. In a
- language that made these "first class" abstraction mechanisms, the difference
- would be more striking, since they'd each have a different syntactic variant.
-
- ==============================================================================
-
- Q93: Should I normally use pointers to freestore allocated objects for my data
- members, or should I use "composition"?
-
- Composition.
-
- Your member objects should normally be "contained" in the composite object (but
- not always; "wrapper" objects are a good example of where you want a ptr/ref;
- also the N-to-1-uses-a relationship needs something like a ptr/ref).
-
- There are three reasons why fully contained member objects ("composition") has
- better performance than ptrs to freestore-allocated member objects:
-
- * Extra layer to indirection every time you need to access the member object.
- * Extra freestore allocations ("new" in constructor, "delete" in destructor).
- * Extra dynamic binding (reason given below).
-
- ==============================================================================
-
- Q94: What are relative costs of the 3 performance hits associated with
- allocating member objects from the freestore?
-
- The three performance hits are enumerated in the previous FAQ:
- * By itself, an extra layer of indirection is small potatoes.
- * Freestore allocations can be a performance issue (the performance of the
- typical implementation of malloc degrades when there are many allocations;
- OO s/w can easily become "freestore bound" unless you're careful).
- * The extra dynamic binding comes from having a ptr rather than an object.
- Whenever the C++ compiler can know an object's EXACT class, virtual fn
- calls can be STATICALLY bound, which allows inlining. Inlining allows
- zillions (would you believe half a dozen :-) optimization opportunities
- such as procedural integration, register lifetime issues, etc. The C++
- compiler can know an object's exact class in three circumstances: local
- variables, global/static variables, and fully-contained member objects.
-
- Thus fully-contained member objects allow significant optimizations that
- wouldn't be possible under the "member objects-by-ptr" approach. This is the
- main reason that languages which enforce reference-semantics have "inherent"
- performance challenges.
-
- NOTE: PLEASE READ THE NEXT THREE FAQs TO GET A BALANCED PERSPECTIVE!
-
- ==============================================================================
-
- Q95: Are "inline virtual" member fns ever actually "inlined"?
-
- Yes but...
-
- A virtual call via a ptr or ref is always resolved dynamically, which can never
- be inlined. Reason: the compiler can't know which actual code to call until
- run-time (i.e., dynamically), since the code may be from a derived class that
- was created after the caller was compiled.
-
- Therefore the only time an inline virtual call can be inlined is when the
- compiler knows the "exact class" of the object which is the target of the
- virtual function call. This can only happen when the compiler has an actual
- object rather than a pointer or reference to an object. I.e., either with a
- local object, a global/static object, or a fully contained object inside a
- composite.
-
- Note that the difference between inlining and non-inlining is normally MUCH
- more significant than the difference between a regular fn call and a virtual fn
- call. For example, the difference between a regular fn call and a virtual fn
- call is often just two extra memory references, but the difference between an
- inline function and a non-inline function can be as much as an order of
- magnitude (for zillions of calls to insignificant member fns, loss of inlining
- virtual fns can result in 25X speed degradation! [Doug Lea, "Customization in
- C++," proc Usenix C++ 1990]).
-
- A practical consequence of this insight: don't get bogged down in the endless
- debates (or sales tactics!) of compiler/language vendors who compare the cost
- of a virtual function call on their language/compiler with the same on another
- language/compiler. Such comparisons are largely meaningless when compared with
- the ability of the language/compiler to "inline expand" member function calls.
- I.e., many language implementation vendors make a big stink about how good
- their dispatch strategy is, but if these implementations don't INLINE method
- calls, the overall system performance would be poor, since it is inlining --NOT
- dispatching-- that has the greatest performance impact.
-
- NOTE: PLEASE READ THE NEXT TWO FAQs TO SEE THE OTHER SIDE OF THIS COIN!
-
- ==============================================================================
-
- Q96: Sounds like I should never use reference semantics, right?
-
- Wrong.
-
- Reference semantics are A Good Thing. We can't live without pointers. We just
- don't want our s/w to be One Gigantic Rats Nest Of Pointers. In C++, you can
- pick and choose where you want reference semantics (ptrs/refs) and where you'd
- like value semantics (where objects physically contain other objects etc). In
- a large system, there should be a balance. However if you implement absolutely
- EVERYTHING as a pointer, you'll get enormous speed hits.
-
- Objects near the problem skin are larger than higher level objects. The
- IDENTITY of these "problem space" abstractions is usually more important than
- their "value." Thus reference semantics should be used for problem-space
- objects.
-
- Note that these problem space objects are normally at a higher level of
- abstraction than the solution space objects, so the problem space objects
- normally have a relatively lower frequency of interaction. Therefore C++ gives
- us an IDEAL situation: we choose reference semantics for objects that need
- unique identity or that are too large to copy, and we can choose value
- semantics for the others. Thus the highest frequency objects will end up with
- value semantics, since we install flexibility only where it doesn't hurt us,
- and we install performance where we need it most!
-
- These are some of the many issues the come into play with real OO design.
- OO/C++ mastery takes time and high quality training. If you want a powerful
- tool, you've got to invest.
-
- <<<<DON'T STOP NOW! READ THE NEXT FAQ TOO!!>>>>
-
- ==============================================================================
-
- Q97: Does the poor performance of ref semantics mean I should pass-by-value?
-
- Nope.
-
- The previous FAQ were talking about MEMBER OBJECTS, not parameters. Generally,
- objects that are part of an inheritance hierarchy should be passed by ref or by
- ptr, NOT by value, since only then do you get the (desired) dynamic binding
- (pass-by-value doesn't mix with inheritance, since larger subclass objects get
- "sliced" when passed by value as a base class object).
-
- Unless compelling reasons are given to the contrary, member objects should be
- by value and parameters should be by reference. The discussion in the previous
- few FAQs indicates some of the "compelling reasons" for when member objects
- should be by reference.
-
- --
- Marshall Cline
- --
- Marshall P. Cline, Ph.D. / Paradigm Shift Inc / PO Box 5108 / Potsdam NY 13676
- cline@sun.soe.clarkson.edu / 315-353-6100 / FAX: 315-353-6110
-